#include "TransparencyHelper.h"

///Copyright (c) 2009-2010 T. Klepzig 

HWINEVENTHOOK TransparencyHelper::hWinEventHook=0;
set<TransparencyHelper*> TransparencyHelper::instances;


TransparencyHelper::TransparencyHelper()
{
	transparentOnMoveSize=false;

	if (instances.empty())
		hWinEventHook=SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_OBJECT_DESTROY,0,WinEventProc,0,0,0);

	instances.insert(this);
}

TransparencyHelper::~TransparencyHelper()
{
	ResetTransparentOnLeaveWindows();

	instances.erase(this);

	if (instances.empty())
		UnhookWinEvent(hWinEventHook);
}

void TransparencyHelper::ToggleTransparentOnLeave(HWND hWnd)
{	
	if (transparentOnLeaveHWnds.find(hWnd) == transparentOnLeaveHWnds.end())
	{
		COLORREF crKey;
		BYTE alpha;
		DWORD flags;
		GetLayeredWindowAttributes(hWnd,&crKey,&alpha,&flags);

		SetLayeredWindowAttributes(hWnd, 0, 255, LWA_ALPHA);
		SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);

		transparentOnLeaveHWnds[hWnd] = alpha;
	}
	else
	{
		SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
		SetLayeredWindowAttributes(hWnd, 0, transparentOnLeaveHWnds[hWnd], LWA_ALPHA);

		transparentOnLeaveHWnds.erase(hWnd);
	}
}

bool TransparencyHelper::IsTransparentOnLeave(HWND hWnd)
{
	return transparentOnLeaveHWnds.find(hWnd) != transparentOnLeaveHWnds.end();
}

void TransparencyHelper::ToggleTransparentOnMoveSize()
{
	transparentOnMoveSize = !transparentOnMoveSize;
}

bool TransparencyHelper::IsTransparentOnMoveSize()
{
	return transparentOnMoveSize;
}

void TransparencyHelper::ToggleExcludeFromTransparentOnMoveSize(HWND hWnd)
{
	if (transparentOnMoveSize)
	{
		if (excludeFromTransparentOnMoveSizeHWnds.find(hWnd) == excludeFromTransparentOnMoveSizeHWnds.end())
			excludeFromTransparentOnMoveSizeHWnds.insert(hWnd);
		else if (excludeFromTransparentOnMoveSizeHWnds.find(hWnd) != excludeFromTransparentOnMoveSizeHWnds.end())
			excludeFromTransparentOnMoveSizeHWnds.erase(hWnd);
	}
}

bool TransparencyHelper::IsExcludedFromTransparentOnMoveSize(HWND hWnd)
{
	return excludeFromTransparentOnMoveSizeHWnds.find(hWnd) != excludeFromTransparentOnMoveSizeHWnds.end();
}

void TransparencyHelper::SetWindowTransparency(HWND hWnd, BYTE alpha)
{
	if (transparentOnLeaveHWnds.find(hWnd) == transparentOnLeaveHWnds.end())
	{
		if (alpha)
		{
			SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
			SetLayeredWindowAttributes(hWnd, 0, alpha, LWA_ALPHA);
		}
		else
		{
			SetLayeredWindowAttributes(hWnd, 0, alpha, LWA_ALPHA);
			SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
		}
	}
	else
	{
		transparentOnLeaveHWnds[hWnd] = alpha;
	}
}


void TransparencyHelper::MakeInactiveWindowsTransparent(HWND foregroundHWnd)
{
	map<HWND,BYTE>::iterator iter;
	for (iter = transparentOnLeaveHWnds.begin();iter!=transparentOnLeaveHWnds.end();iter++)
	{
		if (foregroundHWnd != iter->first)
		{
			SetWindowLongPtr(iter->first, GWL_EXSTYLE, GetWindowLongPtr(iter->first, GWL_EXSTYLE) | WS_EX_LAYERED);
			SetLayeredWindowAttributes(iter->first, 0, iter->second, LWA_ALPHA);
		}
		else
		{
			SetLayeredWindowAttributes(iter->first, 0, 255, LWA_ALPHA);
			SetWindowLongPtr(iter->first, GWL_EXSTYLE, GetWindowLongPtr(iter->first, GWL_EXSTYLE) & ~WS_EX_LAYERED);
		}
	}
}

void TransparencyHelper::ResetTransparentOnLeaveWindows()
{
	map<HWND,BYTE>::iterator iter;
	for (iter = transparentOnLeaveHWnds.begin();iter!=transparentOnLeaveHWnds.end();iter++)
	{
		SetLayeredWindowAttributes(iter->first, 0, 255, LWA_ALPHA);
		SetWindowLongPtr(iter->first, GWL_EXSTYLE, GetWindowLongPtr(iter->first, GWL_EXSTYLE) & ~WS_EX_LAYERED);
	}
}



VOID CALLBACK TransparencyHelper::WinEventProc(HWINEVENTHOOK hWinEventHook,DWORD dwEvent,HWND hWnd,LONG idObject,LONG idChild,DWORD idEventThread,DWORD dwmsEventTime)
{
	static bool wasLayeredWindow=false;
	static COLORREF previousCrKey;
	static BYTE previousAlpha;
	static DWORD previousFlags;

	if ((dwEvent == EVENT_SYSTEM_FOREGROUND || dwEvent == EVENT_SYSTEM_MINIMIZEEND) && idObject == OBJID_WINDOW)
	{
		if (!IsIconic(hWnd))
		{
			set<TransparencyHelper*>::iterator iter;
			for(iter=instances.begin(); iter!=instances.end(); iter++)
			{
				(*iter)->MakeInactiveWindowsTransparent(Utilities::TopLevelWindow(hWnd));
			}
		}
	}
	else if (dwEvent == EVENT_OBJECT_DESTROY && idObject == OBJID_WINDOW)
	{
		set<TransparencyHelper*>::iterator iter;
		for(iter=instances.begin(); iter!=instances.end(); iter++)
		{
			if ((*iter)->IsTransparentOnLeave(hWnd))
				(*iter)->transparentOnLeaveHWnds.erase(hWnd);

			if ((*iter)->excludeFromTransparentOnMoveSizeHWnds.find(hWnd) != (*iter)->excludeFromTransparentOnMoveSizeHWnds.end())
				(*iter)->excludeFromTransparentOnMoveSizeHWnds.erase(hWnd);
		}

		
	}
	else if (dwEvent == EVENT_SYSTEM_MOVESIZESTART && idObject == OBJID_WINDOW)
	{
		set<TransparencyHelper*>::iterator iter;
		for(iter=instances.begin(); iter!=instances.end(); iter++)
		{
			if ((*iter)->transparentOnMoveSize && Utilities::IsValidTaskWindow(hWnd,true,true) && !(*iter)->IsExcludedFromTransparentOnMoveSize(hWnd))
			{
				if ((GetWindowLongPtr(hWnd, GWL_EXSTYLE) & WS_EX_LAYERED) == WS_EX_LAYERED)
				{
					wasLayeredWindow = true;
					GetLayeredWindowAttributes(hWnd, &previousCrKey, &previousAlpha, &previousFlags);
				}
				else
				{
					wasLayeredWindow = false;
					SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
				}

				if (!wasLayeredWindow || previousAlpha > 180)
				{
					SetLayeredWindowAttributes(hWnd, 0, 180, LWA_ALPHA);
				}
			}
		}
	}
	else if (dwEvent == EVENT_SYSTEM_MOVESIZEEND && idObject == OBJID_WINDOW)
	{
		set<TransparencyHelper*>::iterator iter;
		for(iter=instances.begin(); iter!=instances.end(); iter++)
		{
			if ((*iter)->transparentOnMoveSize && Utilities::IsValidTaskWindow(hWnd,true,true) && !(*iter)->IsExcludedFromTransparentOnMoveSize(hWnd))
			{
				if (wasLayeredWindow)
				{
					SetLayeredWindowAttributes(hWnd, previousCrKey, previousAlpha, previousFlags);
				}
				else
				{
					SetLayeredWindowAttributes(hWnd, 0, 255, LWA_ALPHA);
					SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
				}
			}
		}
	}
}


